package pcsr.drivers.epuckDriver.command;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;

public class EPuckCommBlueCove implements IePuckComm, DiscoveryListener{
	private StreamConnection con;
	private OutputStream os;
	private InputStream is;

	private String EPuckBTAddress;
	public int BLUETOOTH;

	// BB Added:
	private static Vector<RemoteDevice> devices;
	private static Vector<EPuckInfo> epuckInfos;
	private EPuckInfo epuckInfo;

	public EPuckCommBlueCove(int BlueTooth) {
		BLUETOOTH = BlueTooth;
	}

	@Override
	public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
		try {
			System.out.println("Found Device,  class: " + cod.getMajorDeviceClass() + "/" + cod.getMinorDeviceClass()+ " --> "+btDevice.getFriendlyName(false));
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (cod.getMajorDeviceClass() == 0 && cod.getMinorDeviceClass() == 0)
			devices.addElement(btDevice);
	}

	@Override
	public synchronized void inquiryCompleted(int discType) {
		if (discType == INQUIRY_COMPLETED) System.out.println("Inquiry completed");
		else System.out.println("Inquiry Failed");
		//notify();
		notifyAll();
	}

	@Override
	public synchronized void serviceSearchCompleted(int transID, int respCode) {
		System.out.println("Service search completed: respCode = " + respCode);
		switch (respCode) {
		case SERVICE_SEARCH_COMPLETED:
			System.out.println("the service search completed normally");
			break;
		case SERVICE_SEARCH_TERMINATED:
			System.out.println("the service search request was cancelled by a call to DiscoveryAgent.cancelServiceSearch()");
			break;
		case SERVICE_SEARCH_ERROR:
			System.out.println("an error occurred while processing the request");
			break;
		case SERVICE_SEARCH_NO_RECORDS:
			System.out.println(" no records were found during the service search");
			break;
		case SERVICE_SEARCH_DEVICE_NOT_REACHABLE:
			System.out.println("the device specified in the search request could not be reached or the local device could not establish a connection to the remote device");
			break;
		default:
			break;
		}

		notifyAll();
	}

	@Override
	public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
		System.out.println(servRecord.length + " service(s) discovered");
		// Should only be one service on a EPUCK
		if (servRecord.length != 1) return;
		epuckInfos.get(0).btResourceString = servRecord[0].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
		System.out.println("Setting url to : " + epuckInfos.get(0).btResourceString);
	}

	@Override
	public void close() {
		try {
			if (os != null) {
				os.close();
			}
			if (is != null) {
				is.close();
			}
			if (con != null) {
				con.close();
			}
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}

	@Override
	public void open() throws Exception {
		String btResourceString = "btspp://" + stripColons(EPuckBTAddress)
		+ ":1;authenticate=false;encrypt=false";

		try {
			con = (StreamConnection) Connector.open(btResourceString);
			os = con.openOutputStream();
			is = con.openInputStream();
		} catch (IOException e) {
			System.err.println("Open of EPuck failed: " + e.getMessage());
			throw e;
		}
	}


	public void getcameradata()
	{
		byte buf[] = new byte[999999];
		byte[] request = new byte[]{(byte) 0xb7,0x00};

		try {
			
			int idx = 0;
			byte c = 0;
			do
			{
				os.write(request);
				// read data from the server	
				c = (byte)is.read();
				buf[idx++] = c;
			}while(idx < 999999 && c != 0 );
System.err.println(idx);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}
	
	public String readData() {
		String reply = "";
		final int BUFF_SIZE = 2000; 

		try {
			char buf[] = new char[BUFF_SIZE];
			int idx = 0;
			char c = '\r';
			do
			{
				// read data from the server	
				c = (char)is.read();
				buf[idx++] = c;
			}while(idx < BUFF_SIZE && c != '\r');

			reply = printResponse(buf, idx);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
		return reply;
	}

	public void SendData(byte[] userRequest, byte packetType, byte opcode )
	{		
		int payload = userRequest.length;

		byte[] requestHeaderPlusCheckSum = getHeaderPlusChecksum(
				packetType,
				opcode,
				payload);	

		int totalLength = userRequest.length + requestHeaderPlusCheckSum.length +2;
		byte[] request = new byte[totalLength];

		request[0] = IePuckProtocol.STX;
		// Put Headers
		for(int i=0; i < requestHeaderPlusCheckSum.length;i++)
		{
			request[1+i] = requestHeaderPlusCheckSum[i];
		}
		// Put Data
		for(int j=0; j < userRequest.length;j++)
		{
			request[requestHeaderPlusCheckSum.length+j+1] = userRequest[j];
		}
		request[totalLength-1] = IePuckProtocol.ETX;

		System.out.println("----------------");
		for(byte b : request )
		{
			System.out.print(java.lang.Integer.toHexString(b) + " , ");
		}
		System.out.println("----------------");
		try {
			os.write(request);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}

	public void setTransparentMode()
	{
		int payload = 1;		
		byte[] requestHeaderPlusCheckSum = getHeaderPlusChecksum(
				IePuckProtocol.PACKETTYPE.REQUEST,
				IePuckProtocol.OPCODE.SPP_TRANSPARENT_MODE,
				payload);	

		byte[] request = {
				IePuckProtocol.STX,
				requestHeaderPlusCheckSum[0], 
				requestHeaderPlusCheckSum[1],
				requestHeaderPlusCheckSum[2], 
				requestHeaderPlusCheckSum[3], 
				requestHeaderPlusCheckSum[4], // Checksum
				0x01, //link number 1-30
				IePuckProtocol.ETX
		};
		try {
			os.write(request);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}

	public void sendData(byte[] request) {
		try {
			os.write(request);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}

	public void sendData(String message) {
		try {
			// send data to the server
			os.write(message.getBytes());
		} catch(IOException e) {
			e.printStackTrace();
		}
	}

	public EPuckInfo[] search(String name, int protocol) {

		devices = new Vector<RemoteDevice>();
		epuckInfos = new Vector<EPuckInfo>();

		if ((protocol | BLUETOOTH) == 0) return new EPuckInfo[0];

		synchronized (this) {
			try {
				LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, this);
				try {
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} catch (BluetoothStateException e) {
				e.printStackTrace();
			}
		}

		for (Enumeration<RemoteDevice> enum_d = devices.elements(); enum_d.hasMoreElements();) {
			RemoteDevice d = (RemoteDevice) enum_d.nextElement();

			try {	
				epuckInfo = new EPuckInfo();

				epuckInfo.name = d.getFriendlyName(false);
				if (epuckInfo.name == null || epuckInfo.name.length() == 0)
					epuckInfo.name = "Unknown";
				epuckInfo.btDeviceAddress = d.getBluetoothAddress();
				epuckInfo.protocol = BLUETOOTH;

				if (name == null || name.equals(epuckInfo.name)) epuckInfos.addElement(epuckInfo);
				else continue;

				System.out.println("Found: " + epuckInfo.name);

				// We want additional attributes, ServiceName (0x100),
				// ServiceDescription (0x101) and ProviderName (0x102).  				

				int[] attributes = {0x100,0x101,0x102};

				UUID[] uuids = new UUID[1];
				uuids[0] = new UUID("1101",true); // Serial Port
				synchronized (this) {
					try {
						LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(attributes,uuids,d,this);
						try {
							wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					} catch (BluetoothStateException e) {
					}
				}

				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			} catch (IOException e) {
				e.printStackTrace();
			}

		}
		EPuckInfo[] nxts = new EPuckInfo[epuckInfos.size()];
		for(int i=0;i<nxts.length;i++) nxts[i] = (EPuckInfo) epuckInfos.elementAt(i);
		return nxts;
	}

	/**
	 * Allows the creation of a EPuckCommBlueCove object without any parameters, then the search method
	 * can be used, and then the BT Address can be set later with this method.
	 * @param address The Bluetooth Address, with or without colors. e.g. "00:16:53:04:a5:9E"
	 */
	public void setAddress(String address) {
		EPuckBTAddress = stripColons(address);
	}

	private String printResponse(char[] buf, int respLenght) {
		String response = "";
		for( char b : buf)
		{
			respLenght--;
			response += b;
			if(respLenght == 0)
				break;
		}
		return response;
	}

	private byte[] getHeaderPlusChecksum(byte packrtType, byte opcode,int payload) {
		byte payloadLow = (byte) payload;
		byte pauloadHigh = (byte) (payload >>> 8);

		byte[] ret = new byte[5];
		ret[0] = packrtType;
		ret[1] = opcode;
		ret[2] = payloadLow;
		ret[3] = pauloadHigh;
		ret[4] = (byte)(ret[0]+ret[1]+ret[2]+ret[3]);

		ret[4] = (byte)(ret[4] & 0xFF);
		return ret;
	}

	private String stripColons(String s) {
		StringBuffer sb = new StringBuffer();

		if(s != null)
		{
			for (int i = 0; i < s.length(); i++) {
				char c = s.charAt(i);

				if (c != ':') {
					sb.append(c);
				}
			}
		}
		return sb.toString();
	}

}
